package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private CartInfoMapper cartInfoMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private CartAsyncService cartAsyncService;

    /*
    1.  添加商品之前，先看一下购物车中是否有该商品
        true:
            商品数量相加
        false:
            直接加入购物车
    2.    将数据同步到redis！

 */
    //  数据类型hash + key hset(key,field,value)
    //  key = user:userId:cart ,谁的购物车 field = skuId value = cartInfo
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        String cartKey = this.getCartKey(userId);

        //  判断缓存中是否有数据,redis中某些sku可能过期，导致跟数据库不一致
        if (!redisTemplate.hasKey(cartKey)){
            //  加载数据库并放入缓存
            this.loadCartCache(userId);}
        //  查询缓存 hget(key,field);
        CartInfo cartInfoExist = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());

//        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
//        cartInfoQueryWrapper.eq("user_id",userId).eq("sku_id",skuId);
//        CartInfo cartInfoExist = cartInfoMapper.selectOne(cartInfoQueryWrapper);
        //购物车有相关信息
        if (null != cartInfoExist){
            cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum() + skuNum);
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            //每次添加购物车，都将购物车内商品的选中状态改为1
            cartInfoExist.setIsChecked(1);
            //cartInfoMapper.updateById(cartInfoExist);
            cartAsyncService.updateCartInfo(cartInfoExist);
        }else {
            //第一次加入购物车
            SkuInfo skuInfo = productFeignClient.getAttrValueList(skuId);
            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setCartPrice(skuInfo.getPrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
            cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());

            //cartInfoMapper.insert(cartInfo);
            cartAsyncService.saveCartInfo(cartInfo);

            cartInfoExist = cartInfo;
        }

        redisTemplate.boundHashOps(cartKey).put(skuId.toString(),cartInfoExist);
        this.setCartKeyExpire(cartKey);

    }

    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        //如果未登录
        if (StringUtils.isEmpty(userId)){
            cartInfoList = this.getCartList(userTempId);
        }
        //如果已经登录,涉及到购物车合并
        if (!StringUtils.isEmpty(userId)){
            //cartInfoList = this.getCartList(userId);
            List<CartInfo> cartInfoNoLoginList = this.getCartList(userTempId);
            //如果未登录购物车有数据
            if (!CollectionUtils.isEmpty(cartInfoNoLoginList)){
                cartInfoList =  this.mergeToCartList(cartInfoNoLoginList, userId);
                // 删除未登录购物车数据
                this.deleteCartList(userTempId);
            }
            //如果未登录购物车无数据，或者未登录ID为空，直接返回已登录购物车内数据
            if (StringUtils.isEmpty(userTempId) || CollectionUtils.isEmpty(cartInfoNoLoginList)){
                cartInfoList = this.getCartList(userId);
            }
        }
        return cartInfoList;
    }

    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        //修改 数据库内 选中状态 （异步）
        cartAsyncService.checkCart(userId,isChecked,skuId);
        // 修改 缓存内 选中的状态
        String cartKey = this.getCartKey(userId);
        if (redisTemplate.hasKey(cartKey)){
            BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
            if (boundHashOperations.hasKey(skuId.toString())){
                CartInfo cartInfo = boundHashOperations.get(skuId.toString());
                cartInfo.setUserId(userId);
                cartInfo.setIsChecked(isChecked);
                cartInfo.setSkuId(skuId);
                boundHashOperations.put(skuId.toString(),cartInfo);
                // 设置过期时间
                this.setCartKeyExpire(cartKey);
            }

        }
    }

    @Override
    public void deleteCart(Long skuId, String userId) {
        //异步删除数据库中的商品信息
        String cartKey = getCartKey(userId);
        cartAsyncService.deleteCartInfo(userId, skuId);
        //删除 缓存中的商品信息
        BoundHashOperations<String,String,CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        if (boundHashOperations.hasKey(skuId.toString())){
            boundHashOperations.delete(skuId.toString());
        }

    }



    //合并后删除 删除未登录购物车数据
    private void deleteCartList(String userTempId) {
        //删除数据库
        cartAsyncService.deleteCartInfo(userTempId);
        //删除缓存
        String cartKey = this.getCartKey(userTempId);
        if (redisTemplate.hasKey(cartKey)){
            redisTemplate.delete(cartKey);
        }
    }

    //合并未登录与登录购物车
    private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoNoLoginList, String userId) {
        List<CartInfo> cartInfoLoginList = this.getCartList(userId);
        // if (!CollectionUtils.isEmpty(cartInfoNoLoginList)){
            //将已登录集合转化为map，存储key：skuId  value：cartInfo
            Map<Long, CartInfo> cartInfoMapLogin = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));

            cartInfoNoLoginList.stream().forEach(cartInfo -> {
                Long skuId = cartInfo.getSkuId();
                //如果map中包含这个skuId，说明未登录购物车中的sku，登录的购物车也有，将数量相加
                if (cartInfoMapLogin.containsKey(skuId)){
                    CartInfo cartInfoLogin = cartInfoMapLogin.get(skuId);
                    cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum() + cartInfo.getSkuNum());
                    cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));
                    //合并数据：勾选
                    // 未登录状态选中的商品如果是1，合并后为状态也为1！
                    if (cartInfo.getIsChecked().intValue() == 1) {
                        cartInfoLogin.setIsChecked(1);
                    }
                    //将更新后的数量 update进数据库
                    cartInfoMapper.update(cartInfoLogin,new QueryWrapper<CartInfo>().eq("user_id",userId)
                    .eq("sku_id",skuId));
                    //cartAsyncService.updateCartInfo(cartInfoLogin);
                }else{
                    //如果map不包含skuId，说明未登录的购物车内有 已登录的购物车内 没有的sku 直接将该信息 保存数据库即可
                    cartInfo.setUserId(userId);
                    cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                    cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                    cartInfoMapper.insert(cartInfo);
                    //cartAsyncService.saveCartInfo(cartInfo);
                }
            });
        // }
        //  从数据库中获取到最新合并的数据，然后放入缓存
        List<CartInfo> cartInfoList = this.loadCartCache(userId);
        //  返回数据
        return cartInfoList;
    }

    private List<CartInfo> getCartList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        //当未登录时传入的是userTempId，如果userTempId也为空，返回空集合
        if (StringUtils.isEmpty(userId)){
            return cartInfoList;
        }
        String cartKey = this.getCartKey(userId);
        //先从缓存取
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoList)){
            // 购物车列表显示有顺序：按照商品的更新时间 降序
            cartInfoList.sort((o1, o2) ->
                    DateUtil.truncatedCompareTo(o2.getUpdateTime(),o1.getUpdateTime(),Calendar.SECOND)
            );
            return cartInfoList;
        }else{
            // 缓存中没有数据！
            cartInfoList = loadCartCache(userId);
            cartInfoList.sort((o1, o2) ->
                    DateUtil.truncatedCompareTo(o2.getUpdateTime(),o1.getUpdateTime(),Calendar.SECOND)
            );
            return cartInfoList;
        }

    }
    // 如果缓存中没数据，从数据库中取，并存入缓存
    @Override
    public List<CartInfo> loadCartCache(String userId) {
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(new QueryWrapper<CartInfo>().eq("user_id", userId));
        //数据库如果没有，返回空对象，防治缓存穿透
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList;
        }
        //  获取key
        String cartKey = this.getCartKey(userId);
        HashMap<String, CartInfo> map = new HashMap<>();
        if (!CollectionUtils.isEmpty(cartInfoList)){
            cartInfoList.stream().forEach(cartInfo ->{
                 cartInfo.setSkuPrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));
                 map.put(cartInfo.getSkuId().toString(),cartInfo);}
                );
        }
        redisTemplate.opsForHash().putAll(cartKey,map);
        //  设置一个过期时间
        this.setCartKeyExpire(cartKey);
        //  返回数据
        return cartInfoList;
    }

    //  设置过期时间
    public void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    //  封装一个获取购物车key 的方法
    public String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX+userId+RedisConst.USER_CART_KEY_SUFFIX;
    }

    //结算时 将选中的商品集合 放入订单页面
    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCachInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartCachInfoList)){
            cartInfoList = cartCachInfoList.stream().map(cartInfo -> {
                if (cartInfo.getIsChecked() == 1){
                    return cartInfo;
                }
                return null;
            }).collect(Collectors.toList());
        }

        return cartInfoList;
    }

}
